package com.orhanobut.hawk.converter

import com.google.gson.reflect.TypeToken
import com.orhanobut.hawk.DataInfo
import com.orhanobut.hawk.parser.Parser

class HawkConverter(private val parser: Parser) : Converter {
    override fun <T> toString(value: T): String? = value?.let { parser.toJson(it as Any) }

    @Suppress("UNCHECKED_CAST")
    override fun <T> fromString(value: String?, info: DataInfo): T? {
        if (value == null) return null
        val keyType = info.keyClazz
        val valueType = info.valueClazz
        return when (info.dataType) {
            DataInfo.TYPE_OBJECT -> parser.fromJson<T>(content = value, clazz = keyType)
            DataInfo.TYPE_LIST -> {
                val listAny =
                    parser.fromJson<List<Any>>(content = value, type = object : TypeToken<List<Any>>() {}.type)
                        ?: emptyList()
                if (keyType == null) {
                    listAny as T
                } else {
                    val result = ArrayList<Any>(listAny.size)
                    for (item in listAny) {
                        val mapped = parser.fromJson<Any>(content = parser.toJson(item), clazz = keyType)
                        if (mapped != null) result.add(mapped)
                    }
                    result as T
                }
            }

            DataInfo.TYPE_MAP -> {
                val mapAny =
                    parser.fromJson<Map<Any, Any>>(content = value, type = object : TypeToken<Map<Any, Any>>() {}.type)
                        ?: emptyMap()
                if (keyType == null || valueType == null) {
                    mapAny as T
                } else {
                    val result = LinkedHashMap<Any, Any>(mapAny.size)
                    for ((k, v) in mapAny.entries) {
                        val rk = parser.fromJson<Any>(content = parser.toJson(k), clazz = keyType)
                        val rv = parser.fromJson<Any>(content = parser.toJson(v), clazz = valueType)
                        if (rk != null && rv != null) result[rk] = rv
                    }
                    result as T
                }
            }

            DataInfo.TYPE_SET -> {
                val setAny = parser.fromJson<Set<Any>>(content = value, type = object : TypeToken<Set<Any>>() {}.type)
                    ?: emptySet()
                if (keyType == null) {
                    setAny as T
                } else {
                    val result = LinkedHashSet<Any>(setAny.size)
                    for (t in setAny) {
                        val mapped = parser.fromJson<Any>(content = parser.toJson(t), clazz = keyType)
                        if (mapped != null) result.add(mapped)
                    }
                    result as T
                }
            }

            else -> null
        }
    }
}
